import inspect
import os

from utils.base_util import CommonUtils


class Tester:
    '''
    测试器类
    '''
    @classmethod
    def run_all_testmethods(cls, testcases_dir_name):
        '''
        执行测试用例顶级目录下的所有系统模块下的所有测试类中的所有测试方法
        :param testcases_dir_name:测试用例顶级目录
        :return:
        '''
        project_path = CommonUtils.get_project_path()
        # 测试用例目录的绝对路径
        testcases_path = os.path.join(project_path, testcases_dir_name)
        # 获取测试用例目录下的测试模块列表
        list_testmodules = cls.get_testmodules(testcases_path)
        # 遍历测试模块列表
        for testmodule in list_testmodules:
            # 获取测试模块下的测试类列表
            list_testclasses = cls.get_testclasses(testmodule[0], testmodule[1])
            # 遍历测试类列表
            for testclass in list_testclasses:
                # 获取测试类中的测试方法列表
                testclass_object = testclass[0]()
                list_testmethods = cls.get_testmethods(testclass_object)
                print(f"{testclass[1]}" + "=" * 30)
                # 遍历测试方法列表
                for testmethod in list_testmethods:
                    # 执行测试方法
                    print("*" * 30)
                    testmethod()
                    print("*" * 30)
                print(f"{testclass[1]}" + "=" * 30)

    @classmethod
    def run_specified_testmethods(cls, testcases_dir_name):
        '''
        执行测试用例顶级目录下的指定模块下的指定测试类中的指定测试方法
        :param testcases_dir_name:
        :return:
        '''

        testmethods_list = CommonUtils.read_ini_by_option("config/test_list.config","test_list","methods")
        project_path = CommonUtils.get_project_path()
        # 测试用例目录的绝对路径
        testcases_path = os.path.join(project_path, testcases_dir_name)
        # 获取测试用例目录下的测试模块列表
        list_testmodules = cls.get_testmodules(testcases_path)
        # 遍历测试模块列表
        for testmodule in list_testmodules:
            # 获取测试模块下的测试类列表
            list_testclasses = cls.get_testclasses(testmodule[0], testmodule[1])
            # 遍历测试类列表
            for testclass in list_testclasses:
                # 获取测试类中的测试方法列表
                testclass_object = testclass[0]()
                list_testmethods = cls.get_testmethods(testclass_object, testclass_fullname=testclass[1], testmethods_list=testmethods_list)
                print(f"{testclass[1]}" + "="*30)
                # 遍历测试方法列表
                for testmethod in list_testmethods:
                    # 执行测试方法
                    print("*" * 30)
                    testmethod()
                    print("*" * 30)
                print(f"{testclass[1]}" + "="*30)


    @classmethod
    def get_sysmodules(cls, testcases_path, condition_method="endswith", condition_str="test"):
        '''
        传入测试用例类的顶级目录的目录名,返回顶级目录下的测试模块的列表
        :param testcases_path:
        :return: type:list[str]
        '''
        dirs = os.listdir(testcases_path)
        list_dir = []
        for item in dirs:

            if item.endswith("test"):
                list_dir.append(item)
        return list_dir

    @classmethod
    def get_testmodules(cls, testcases_path):
        '''
        获取系统模块下的测试模块列表
        :param sysmodule:
        :return:
        '''
        dirs = os.listdir(testcases_path)
        list_modules = []
        for item in dirs:
            if item.endswith("test.py"):
                # 拼接测试模块的相对路径
                testcases_name = os.path.basename(testcases_path)
                # eg："abc.py"的前缀"abc"
                prefix = item.split(".")[0]

                testmodule_full_name = testcases_name + "." + prefix
                testmodule = __import__(testmodule_full_name, fromlist=[prefix])
                # [[模块对象,模块名(不带.py)]]
                list_modules.append([testmodule, prefix])
        return list_modules

    @classmethod
    def get_testclasses(cls, testmodule, testmodule_name):
        '''
        获取测试模块下的测试类列表
        :param testmodule:
        :return:
        '''
        list_classes = []
        classes = inspect.getmembers(testmodule, inspect.isclass)
        for item in classes:
            if item[0].endswith("Test"):
                class_fullname = testmodule_name + "." + item[0]
                # [[类对象，模块名.类名]]
                list_classes.append([item[1], class_fullname])

        return list_classes

    @classmethod
    def get_testmethods(cls, testclass_object, testclass_fullname=None, testmethods_list=None):
        '''
        获取测试类中的测试方法列表,先添加setup,再添加test开头的方法,最后添加teardown
        :param testclass_object:
        :param testclass_fullname:模块名.类名
        :param testmethods_list:
        :return:
        '''
        list_result = []
        if testmethods_list is None:
            testmethods_list = inspect.getmembers(testclass_object, inspect.ismethod)
            for item in testmethods_list:
                if item[0].startswith("test"):
                    if hasattr(testclass_object, "setup"):
                        # 添加setup方法对象
                        list_result.append(getattr(testclass_object, "setup"))

                    # 添加测试方法对象
                    list_result.append(item[1])

                    if hasattr(testclass_object, "teardown"):
                        # 添加teardown方法对象
                        list_result.append(getattr(testclass_object, "teardown"))
        else:
            for testmethod in testmethods_list:
                target_strs = testmethod.split(".")
                real_strs = testclass_fullname.split(".")
                if target_strs[0] == real_strs[0] and target_strs[1] == real_strs[1] and hasattr(testclass_object, target_strs[2]):
                    if hasattr(testclass_object, "setup"):
                        # 添加setup方法对象
                        list_result.append(getattr(testclass_object, "setup"))
                    method_attr = getattr(testclass_object, target_strs[2])
                    list_result.append(method_attr)
                    if hasattr(testclass_object, "teardown"):
                        # 添加teardown方法对象
                        list_result.append(getattr(testclass_object, "teardown"))


        return list_result


if __name__ == '__main__':
    # test_list = CommonUtils.read_ini_by_option("config/test_list.config", "test_list", "methods")
    # print(test_list)
    # Tester.run_specified_testmethods("testcases")
    Tester.run_all_testmethods("testcases")